package top.rainbowecho.algo.list;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * @author rainbow
 * @since 2021/3/11 15:47
 */
public class SingleLinkedList<T> implements List<T> {
    private final Node<T> head = new Node<>();
    private final Node<T> tail = new Node<>();
    private int size = 0;

    public SingleLinkedList() {
        this.head.next = this.tail;
        this.tail.pre = this.head;
    }

    @Override
    public int size() {
        return this.size;
    }

    @Override
    public boolean isEmpty() {
        return this.size == 0;
    }

    @Override
    public boolean contains(Object o) {
        return traverseOp(node -> node.equals(o));
    }

    @Override
    public Iterator<T> iterator() {
        return null;
    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T1> T1[] toArray(T1[] a) {
        return null;
    }

    /**
     * tail insert
     *
     * @param t elem
     * @return insert result
     */
    @Override
    public boolean add(T t) {
        Node<T> node = new Node<>();
        node.val = t;

        if (this.size == 0) {
            insertOne(this.head, this.tail, node);
            this.size++;
            return true;
        }
        Node<T> p = this.tail.pre;
        insertOne(p, this.tail, node);
        this.size++;
        return true;
    }


    /**
     * insert one item in two nodes
     *
     * @param head node which before tail node
     * @param tail tail node
     * @param item item will be inserted
     */
    private void insertOne(Node<T> head, Node<T> tail, Node<T> item) {
        head.next = item;
        item.pre = head;
        item.next = tail;
        tail.pre = item;
    }

    @Override
    public boolean remove(Object o) {
        return traverseOp(node -> {
            if (node.equals(o)) {
                Node<T> p = node.pre;
                p.next = node.next;
                node.next.pre = p;
                this.size--;
                return true;
            }
            return false;
        });
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        Node<T> p = isEmpty() ? this.head : this.tail.pre;
        addMany(c, p);
        return true;
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> c) {
        Node<T> p = this.head;
        if (!isEmpty()) {
            for (int i = 0; i <= index; i++) {
                p = p.next;
            }
        }
        addMany(c, p);
        return true;
    }

    private void addMany(Collection<? extends T> c, Node<T> p) {
        for (T val : c) {
            Node<T> item = new Node<>();
            item.val = val;
            insertOne(p, this.tail, item);
            this.size++;
        }
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return false;
    }

    @Override
    public void clear() {
        this.head.next = this.tail;
        this.tail.pre = this.head;
        this.size = 0;
    }

    @Override
    public T get(int index) {
        if (index + 1 > this.size) {
            return null;
        }
        Node<T> p = this.head;
        for (int i = 0; i <= index; i++) {
            p = p.next;
        }
        return p.val;
    }

    @Override
    public T set(int index, T element) {
        return null;
    }

    @Override
    public void add(int index, T element) {

    }

    @Override
    public T remove(int index) {
        return null;
    }

    @Override
    public int indexOf(Object o) {
        return 0;
    }

    @Override
    public int lastIndexOf(Object o) {
        return 0;
    }

    @Override
    public ListIterator<T> listIterator() {
        return null;
    }

    @Override
    public ListIterator<T> listIterator(int index) {
        return null;
    }

    @Override
    public List<T> subList(int fromIndex, int toIndex) {
        return null;
    }

    public boolean traverse() {
        return traverseOp(node -> {
            System.out.print(node.val + " ");
            return false;
        });
    }

    /**
     * do some operation in traverse
     *
     * @param consumer operation lambda expression
     * @return operate result
     */
    private boolean traverseOp(TraverseConsumer<Node<T>> consumer) {
        if (isEmpty()) {
            return false;
        }
        Node<T> p = this.head.next;
        for (int i = 0; i < size; i++) {
            if (consumer.apply(p)) {
                return true;
            }
            p = p.next;
        }
        return false;
    }

    @FunctionalInterface
    private interface TraverseConsumer<N> {
        /**
         * consume operation in traverse
         *
         * @param node current node in traverse
         * @return result of lambda expression call
         */
        boolean apply(N node);
    }

    private class Node<T> {
        private T val;
        private Node<T> pre;
        private Node<T> next;
    }
}
